#pragma once

#include <string>
#include <unordered_map>
#include <regex>
#include <iostream>

#include "../source/Buffer.hpp"
#include "../source/Server.hpp"
#include "Util.hpp"

#define DEFALT_TIMEOUT 30 // 连接默认定时销毁30s

// http请求
class HttpRequest
{
public:
    std::string _method;                                   // 请求方法
    std::string _path;                                     // 资源路径
    std::string _version;                                  // 协议版本
    std::string _body;                                     // 请求正文
    std::smatch _matches;                                  // 资源路径的正则提取数据
    std::unordered_map<std::string, std::string> _headers; // 头部字段
    std::unordered_map<std::string, std::string> _params;  // 请求行查询字符串
public:
    HttpRequest() : _version("HTTP/1.1") {}

    // 重置
    void ReSet()
    {
        _method.clear();
        _path.clear();
        _version = "HTTP/1.1";
        _body.clear();
        std::smatch match;
        _matches.swap(match);
        _headers.clear();
        _params.clear();
    }

    // 插入头部字段
    void SetHeader(const std::string &key, const std::string &val)
    {
        _headers.insert(std::make_pair(key, val));
    }

    // 判断是否存在指定头部字段
    bool HasHeader(const std::string &key) const
    {
        auto it = _headers.find(key);
        if (it == _headers.end())
        {
            return false;
        }
        return true;
    }

    // 获取指定头部字段的值
    std::string GetHeader(const std::string &key) const
    {
        auto it = _headers.find(key);
        if (it == _headers.end())
        {
            return "";
        }
        return it->second;
    }

    // 请求行插入查询字符串
    void SetParam(const std::string &key, const std::string &val)
    {
        _params.insert(std::make_pair(key, val));
    }

    // 判断是否有某个指定的查询字符串
    bool HasParam(const std::string &key) const
    {
        auto it = _params.find(key);
        if (it == _params.end())
        {
            return false;
        }
        return true;
    }

    // 获取指定的查询字符串
    std::string GetParam(const std::string &key) const
    {
        auto it = _params.find(key);
        if (it == _params.end())
        {
            return "";
        }
        return it->second;
    }

    // 获取正文长度
    size_t ContentLength() const
    {
        // Content-Length: 1234\r\n
        bool ret = HasHeader("Content-Length");
        if (ret == false)
        {
            return 0;
        }
        std::string clen = GetHeader("Content-Length");
        return std::stol(clen);
    }

    // 判断是否是短链接
    bool Close() const
    {
        // 没有Connection字段，或者有Connection但是值是close，则都是短链接，否则就是长连接
        if (HasHeader("Connection") == true && GetHeader("Connection") == "keep-alive")
        {
            return false;
        }
        return true;
    }
};

// http响应
class HttpResponse
{
public:
    int _statu;                                            // 响应状态码
    bool _redirect_flag;                                   // 是否重定向
    std::string _body;                                     // 响应正文
    std::string _redirect_url;                             // 重定向url
    std::unordered_map<std::string, std::string> _headers; // 头部字段
public:
    HttpResponse() : _redirect_flag(false), _statu(200) {}
    HttpResponse(int statu) : _redirect_flag(false), _statu(statu) {}
    void ReSet()
    {
        _statu = 200;
        _redirect_flag = false;
        _body.clear();
        _redirect_url.clear();
        _headers.clear();
    }

    // 插入头部字段
    void SetHeader(const std::string &key, const std::string &val)
    {
        _headers.insert(std::make_pair(key, val));
    }

    // 判断是否存在指定头部字段
    bool HasHeader(const std::string &key)
    {
        auto it = _headers.find(key);
        if (it == _headers.end())
        {
            return false;
        }
        return true;
    }

    // 获取指定头部字段的值
    std::string GetHeader(const std::string &key)
    {
        auto it = _headers.find(key);
        if (it == _headers.end())
        {
            return "";
        }
        return it->second;
    }

    // 响应正文
    void SetContent(const std::string &body, const std::string &type = "text/html")
    {
        _body = body;
        SetHeader("Content-Type", type);
    }

    // 重定向
    void SetRedirect(const std::string &url, int statu = 302)
    {
        _statu = statu;
        _redirect_flag = true;
        _redirect_url = url;
    }

    // 判断是否是短链接
    bool Close()
    {
        // 没有Connection字段，或者有Connection但是值是close，则都是短链接，否则就是长连接
        if (HasHeader("Connection") == true && GetHeader("Connection") == "keep-alive")
        {
            return false;
        }
        return true;
    }
};

typedef enum
{
    RECV_HTTP_ERROR, // 接收错误
    RECV_HTTP_LINE,  // 接收请求行
    RECV_HTTP_HEAD,  // 接收头部
    RECV_HTTP_BODY,  // 接收正文
    RECV_HTTP_OVER   // 接收结束
} HttpRecvStatu;     // http响应阶段

#define MAX_LINE 8192 // 8k
// http报文（解析完成） +  解析后的响应状态码（未业务处理）
class HttpContext
{
private:
    int _respStatu;           // 响应状态码
    HttpRecvStatu _recvStatu; // 接收当前所处阶段
    HttpRequest _request;     // 请求解析后的存放
private:
    // 分析请求行
    bool ParseHttpLine(const std::string &line)
    {
        DLOG("line:%s", line.c_str());
        std::smatch matches;
        std::regex e("(GET|HEAD|POST|PUT|DELETE) ([^?]*)(?:\\?(.*))? (HTTP/1\\.[01])(?:\n|\r\n)?", std::regex::icase);
        bool ret = std::regex_match(line, matches, e);
        if (ret == false)
        {
            _recvStatu = RECV_HTTP_ERROR;
            _respStatu = 400; // BAD REQUEST
            return false;
        }

        // 0 : GET /helloword/login?user=xiaoming&pass=123123 HTTP/1.1
        // 1 : GET
        // 2 : /helloword/login
        // 3 : user=xiaoming&pass=123123
        // 4 : HTTP/1.1

        _request._method = matches[1];
        DLOG("_request._method:%s", _request._method.c_str());
        std::transform(_request._method.begin(), _request._method.end(), _request._method.begin(), toupper);
        _request._path = Util::UrlDecode(matches[2], false);

        DLOG("_request._path:%s", _request._path.c_str());

        std::vector<std::string> query_string_arry;
        std::string query_string = matches[3];

        DLOG("query_string:%s", query_string.c_str());

        // 查询字符串的格式 key=val&key=val....., 先以 & 符号进行分割，得到各个字串
        Util::Split(query_string, "&", &query_string_arry);
        // 针对各个字串，以 = 符号进行分割，得到key 和val， 得到之后也需要进行URL解码
        for (auto &str : query_string_arry)
        {
            DLOG("str:%s", str.c_str());
            size_t pos = str.find("=");
            if (pos == std::string::npos)
            {
                _recvStatu = RECV_HTTP_ERROR;
                _respStatu = 400; // BAD REQUEST
                return false;
            }
            std::string key = Util::UrlDecode(str.substr(0, pos), true);
            std::string val = Util::UrlDecode(str.substr(pos + 1), true);
            _request.SetParam(key, val);
        }

        _request._version = matches[4];
        return true;
    }

    // 获取请求行
    bool RecvHttpLine(Buffer *buf)
    {
        // 不是当前阶段
        if (_recvStatu != RECV_HTTP_LINE)
            return false;

        // 获取一行数据，末尾有换行
        std::string line = buf->GetLineAndPop();
        // 判断是否有有一行/是否完整请求行/请求行过大
        if (line.size() == 0)
        {
            // 缓冲区中的数据不足一行，则需要判断缓冲区的可读数据长度，如果很长了都不足一行，这是有问题的
            if (buf->ReadAbleSize() > MAX_LINE)
            {
                _recvStatu = RECV_HTTP_ERROR;
                _respStatu = 414; // URI TOO LONG
                return false;
            }
            // 不足一行（没有换行符）
            return true;
        }

        //防止独立的请求行过大
        if (line.size() > MAX_LINE)
        {
            _recvStatu = RECV_HTTP_ERROR;
            _respStatu = 414; // URI TOO LONG
            return false;
        }

        // 分析
        bool ret = ParseHttpLine(line);
        if (ret == false)
        {
            return false;
        }
        // 首行处理完毕，进入头部获取阶段
        _recvStatu = RECV_HTTP_HEAD;
        return true;
    }

    // 获取请求头部
    bool RecvHttpHead(Buffer *buf)
    {
        // 获取请求头部前，需要获取请求行
        if (_recvStatu != RECV_HTTP_HEAD)
            return false;
        // 请求行内KV对不知道多少（直到空行）
        while (1)
        {

            std::string line = buf->GetLineAndPop();
            if (line.size() == 0)
            {
                // 缓冲区中的数据不足一行，则需要判断缓冲区的可读数据长度，如果很长了都不足一行，这是有问题的
                if (buf->ReadAbleSize() > MAX_LINE)
                {
                    _recvStatu = RECV_HTTP_ERROR;
                    _respStatu = 414; // URI TOO LONG
                    return false;
                }
                // 缓冲区中数据不足一行，但是也不多，就等等新数据的到来
                return true;
            }
            if (line.size() > MAX_LINE)
            {
                _recvStatu = RECV_HTTP_ERROR;
                _respStatu = 414; // URI TOO LONG
                return false;
            }
            if (line == "\n" || line == "\r\n")
            {
                break;
            }
            bool ret = ParseHttpHead(line);
            if (ret == false)
            {
                return false;
            }
        }
        // 头部处理完毕，进入正文获取阶段
        _recvStatu = RECV_HTTP_BODY;
        return true;
    }

    // 分析请求头部
    bool ParseHttpHead(std::string &line)
    {
        // key: val\r\nkey: val\r\n....
        if (line.back() == '\n')
            line.pop_back(); // 末尾是换行则去掉换行字符
        if (line.back() == '\r')
            line.pop_back(); // 末尾是回车则去掉回车字符
        size_t pos = line.find(": ");
        if (pos == std::string::npos)
        {
            _recvStatu = RECV_HTTP_ERROR;
            _respStatu = 400; //
            return false;
        }
        std::string key = line.substr(0, pos);
        std::string val = line.substr(pos + 2);
        _request.SetHeader(key, val);
        return true;
    }

    // 接收请求体
    bool RecvHttpBody(Buffer *buf)
    {
        if (_recvStatu != RECV_HTTP_BODY)
            return false;
        // 1. 获取正文长度
        size_t content_length = _request.ContentLength();
        if (content_length == 0)
        {
            // 没有正文，则请求接收解析完毕
            _recvStatu = RECV_HTTP_OVER;
            return true;
        }
        // 2. 当前已经接收了多少正文,其实就是往  _request._body 中放了多少数据了
        size_t real_len = content_length - _request._body.size(); // 实际还需要接收的正文长度
        // 3. 接收正文放到body中，但是也要考虑当前缓冲区中的数据，是否是全部的正文
        //   3.1 缓冲区中数据，包含了当前请求的所有正文，则取出所需的数据
        if (buf->ReadAbleSize() >= real_len)
        {
            _request._body.append(buf->ReadPosition(), real_len);
            buf->MoveReadOffset(real_len);
            _recvStatu = RECV_HTTP_OVER;
            return true;
        }
        //  3.2 缓冲区中数据，无法满足当前正文的需要，数据不足，取出数据，然后等待新数据到来
        _request._body.append(buf->ReadPosition(), buf->ReadAbleSize());
        buf->MoveReadOffset(buf->ReadAbleSize());
        return true;
    }

public:
    HttpContext() : _respStatu(200), _recvStatu(RECV_HTTP_LINE) {}

    // 重置
    void ReSet()
    {
        _respStatu = 200;
        _recvStatu = RECV_HTTP_LINE;
        _request.ReSet();
    }

    // 响应状态
    int RespStatu() { return _respStatu; }

    // 接收阶段
    HttpRecvStatu RecvStatu() { return _recvStatu; }

    // 请求解析存放的变量
    HttpRequest &Request() { return _request; }

    // 接收并解析HTTP请求
    void RecvHttpRequest(Buffer *buf)
    {
        // 不同的状态，做不同的事情，但是这里不要break， 因为处理完请求行后，应该立即处理头部，而不是退出等新数据
        switch (_recvStatu)
        {
        case RECV_HTTP_LINE:
            RecvHttpLine(buf);
        case RECV_HTTP_HEAD:
            RecvHttpHead(buf);
        case RECV_HTTP_BODY:
            RecvHttpBody(buf);
        }

        return;
    }
};

class HttpServer
{
private:
    using Handler = std::function<void(const HttpRequest &, HttpResponse *)>; // 处理函数对应的请求与响应
    using Handlers = std::vector<std::pair<std::regex, Handler>>;             // (路径)正则表达式 & 对应处理函数
    Handlers _getRoute;                                                       // GET方法路由表
    Handlers _postRoute;                                                      // POST方法路由表
    Handlers _putRoute;                                                       // PUT方法路由表
    Handlers _deleteRoute;                                                    // DELETE方法路由表
    std::string _basedir;                                                     // 静态资源根目录
    TcpServer _server;

private:
    // 将HttpResponse中的要素按照http协议格式进行组织，发送
    void WriteReponse(const PtrConnection &conn, const HttpRequest &req, HttpResponse &rsp)
    {
        // 1. 先完善头部字段
        if (req.Close() == true)
        {
            rsp.SetHeader("Connection", "close");
        }
        else
        {
            rsp.SetHeader("Connection", "keep-alive");
        }
        if (rsp._body.empty() == false && rsp.HasHeader("Content-Length") == false)
        {
            rsp.SetHeader("Content-Length", std::to_string(rsp._body.size()));
        }
        if (rsp._body.empty() == false && rsp.HasHeader("Content-Type") == false)
        {
            rsp.SetHeader("Content-Type", "application/octet-stream");
        }
        if (rsp._redirect_flag == true)
        {
            rsp.SetHeader("Location", rsp._redirect_url);
        }
        // 2. 将rsp中的要素，按照http协议格式进行组织
        std::stringstream rsp_str;
        rsp_str << req._version << " " << std::to_string(rsp._statu) << " " << Util::StatuDesc(rsp._statu) << "\r\n";
        for (auto &head : rsp._headers)
        {
            rsp_str << head.first << ": " << head.second << "\r\n";
        }
        rsp_str << "\r\n";
        rsp_str << rsp._body;
        // 3. 发送数据
        conn->Send(rsp_str.str().c_str(), rsp_str.str().size());
    }

    // 静态资源的请求处理 --- 将静态资源文件的数据读取出来，放到rsp的_body中, 并设置mime
    void FileHandler(const HttpRequest &req, HttpResponse *rsp)
    {
        std::string req_path = _basedir + req._path;
        if (req._path.back() == '/')
        {
            req_path += "index.html";
        }
        bool ret = Util::ReadFile(req_path, &rsp->_body);
        if (ret == false)
        {
            return;
        }
        std::string mime = Util::ExtMime(req_path);
        rsp->SetHeader("Content-Type", mime);
        return;
    }

    // 是否为静态资源请求
    bool IsFileHandler(const HttpRequest &req)
    {
        // 1. 必须设置了静态资源根目录
        if (_basedir.empty())
        {
            return false;
        }
        // 2. 请求方法，必须是GET / HEAD请求方法
        if (req._method != "GET" && req._method != "HEAD")
        {
            return false;
        }
        // 3. 请求的资源路径必须是一个合法路径
        if (Util::ValidPath(req._path) == false)
        {
            return false;
        }
        // 4. 请求的资源必须存在,且是一个普通文件
        //    有一种请求比较特殊 -- 目录：/, /image/， 这种情况给后边默认追加一个 index.html
        // index.html    /image/a.png
        // 不要忘了前缀的相对根目录,也就是将请求路径转换为实际存在的路径  /image/a.png  ->   ./wwwroot/image/a.png
        std::string req_path = _basedir + req._path; // 为了避免直接修改请求的资源路径，因此定义一个临时对象
        if (req._path.back() == '/')
        {
            req_path += "index.html";
        }
        if (Util::IsRegular(req_path) == false)
        {
            return false;
        }
        return true;
    }

    // 功能请求的分类发送
    void Dispatcher(HttpRequest &req, HttpResponse *rsp, Handlers &handlers)
    {
        // 在对应请求方法的路由表中，查找是否含有对应资源请求的处理函数，有则调用，没有则发挥404
        // 思想：路由表存储的时键值对 -- 正则表达式 & 处理函数
        // 使用正则表达式，对请求的资源路径进行正则匹配，匹配成功就使用对应函数进行处理
        //   /numbers/(\d+)       /numbers/12345
        for (auto &handler : handlers)
        {
            const std::regex &re = handler.first;
            const Handler &functor = handler.second;
            bool ret = std::regex_match(req._path, req._matches, re);
            if (ret == false)
            {
                continue;
            }
            return functor(req, rsp); // 传入请求信息，和空的rsp，执行处理函数
        }
        rsp->_statu = 404;
    }

    // 请求路由
    void Route(HttpRequest &req, HttpResponse *rsp)
    {
        // 1. 对请求进行分辨，是一个静态资源请求，还是一个功能性请求
        //    静态资源请求，则进行静态资源的处理
        //    功能性请求，则需要通过几个请求路由表来确定是否有处理函数
        //    既不是静态资源请求，也没有设置对应的功能性请求处理函数，就返回405
        if (IsFileHandler(req) == true)
        {
            // 是一个静态资源请求, 则进行静态资源请求的处理
            return FileHandler(req, rsp);
        }
        if (req._method == "GET" || req._method == "HEAD")
        {
            return Dispatcher(req, rsp, _getRoute);
        }
        else if (req._method == "POST")
        {
            return Dispatcher(req, rsp, _postRoute);
        }
        else if (req._method == "PUT")
        {
            return Dispatcher(req, rsp, _putRoute);
        }
        else if (req._method == "DELETE")
        {
            return Dispatcher(req, rsp, _deleteRoute);
        }
        rsp->_statu = 405; // Method Not Allowed
        return;
    }

    // 新连接到达调用（设置上下文/协议报文）
    void OnConnected(const PtrConnection &conn)
    {
        conn->SetContext(HttpContext());
        DLOG("NEW CONNECTION %p", conn.get());
    }

    // 缓冲区数据解析+处理
    void OnMessage(const PtrConnection &conn, Buffer *buffer)
    {
        // 默认长连接（一直处理，直到连接主动关闭/错误）
        while (buffer->ReadAbleSize() > 0)
        {
            // 1、获取上下文
            HttpContext *context = conn->GetContext()->get<HttpContext>(); // http报文

            // 2、通过上下文对缓存区解析，并得到HttpRequest
            context->RecvHttpRequest(buffer);       // http报文的请求解析并接收
            HttpRequest &req = context->Request();  // 请求解析后，存放它的变量
            HttpResponse rsp(context->RespStatu()); // http响应实例生成
            if (context->RespStatu() >= 400)
            {
                // 进行错误响应，关闭连接
                ErrorHandler(req, &rsp);      // 填充一个错误显示页面数据到rsp中
                WriteReponse(conn, req, rsp); // 组织响应发送给客户端
                context->ReSet();
                buffer->MoveReadOffset(buffer->ReadAbleSize()); // 出错了就把缓冲区数据清空
                conn->Shutdown();                               // 关闭连接
                return;
            }
            if (context->RecvStatu() != RECV_HTTP_OVER)
            {
                // 当前请求还没有接收完整,则退出，等新数据到来再调用重新继续处理
                return;
            }
            // 3、请求路由 + 对应业务处理
            Route(req, &rsp);
            // 4、对HttpResponse进行组织发送
            WriteReponse(conn, req, rsp);
            // 5、 重置上下文
            context->ReSet();
            // 6. 根据长短连接判断是否关闭连接或者继续处理
            if (rsp.Close() == true)
                conn->Shutdown(); // 短链接则直接关闭
        }
        return;
    }

    // 错误响应（简单的错误页面）
    void ErrorHandler(const HttpRequest &req, HttpResponse *rsp)
    {
        // 1. 组织一个错误展示页面
        std::string body;
        body += "<html>";
        body += "<head>";
        body += "<meta http-equiv='Content-Type' content='text/html;charset=utf-8'>";
        body += "</head>";
        body += "<body>";
        body += "<h1>";
        body += std::to_string(rsp->_statu);
        body += " ";
        body += Util::StatuDesc(rsp->_statu);
        body += "</h1>";
        body += "</body>";
        body += "</html>";
        // 2. 将页面数据，当作响应正文，放入rsp中
        rsp->SetContent(body, "text/html");
    }

public:
    HttpServer(int port, int timeout = DEFALT_TIMEOUT) : _server(port)
    {
        _server.EnableInactiveRelease(timeout);
        _server.SetConnectedCallback(std::bind(&HttpServer::OnConnected, this, std::placeholders::_1));
        _server.SetMessageCallback(std::bind(&HttpServer::OnMessage, this, std::placeholders::_1, std::placeholders::_2));
    }

    // 设置根目录
    void SetBaseDir(const std::string &path)
    {
        assert(Util::IsDirectory(path) == true);
        _basedir = path;
    }

    /*设置/添加，请求（请求的正则表达）与处理函数的映射关系*/

    void Get(const std::string &pattern, const Handler &handler)
    {
        _getRoute.push_back(std::make_pair(std::regex(pattern), handler));
    }

    void Post(const std::string &pattern, const Handler &handler)
    {
        _postRoute.push_back(std::make_pair(std::regex(pattern), handler));
    }

    void Put(const std::string &pattern, const Handler &handler)
    {
        _putRoute.push_back(std::make_pair(std::regex(pattern), handler));
    }

    void Delete(const std::string &pattern, const Handler &handler)
    {
        _deleteRoute.push_back(std::make_pair(std::regex(pattern), handler));
    }

    void SetThreadCount(int count)
    {
        _server.SetThreadCount(count);
    }

    void Listen()
    {
        _server.Start();
    }
};
